﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;

namespace ComponentRefCreator.Core
{
    /// <summary>
    /// Provides the necessary tools to parse a Wix Xml file
    /// </summary>
    public class WixParser
    {
        public string Xml { get; set; }
        private List<ComponentRef> ComponentRefs { get; set; }

        public WixParser()
        {
            ComponentRefs = new List<ComponentRef>();
        }

        public WixParser(string xml):this()
        {
            Xml = xml;
        }

        /// <summary>
        /// Determines if the Xml is valid for Wix processing
        /// </summary>
        /// <returns>return true if it conforms to Wix schema, otherwise returns false</returns>
        public bool IsValidWixXml()
        {
            bool valid = false;

            if (!string.IsNullOrEmpty(Xml))
            {
                XmlDocument document = new XmlDocument();

                // Check and see if the string passed in is valid Xml
                // if not return false;
                try 
	            {	        
                    document.LoadXml(Xml);
                }
                catch (System.Exception exception)
                {
                    Debug.WriteLine(exception.ToString());
                }

                // Check to see if we have any nodes and if the first node is the XML header
                if(document.ChildNodes.Count > 0 && 
                    document.ChildNodes[0].Name.ToLower() == "xml")
                {
                    // Check to see if we have another node, otherwise we have an empty xml document
                    if(document.ChildNodes.Count > 1)
                    {
                        if (document.ChildNodes[1].Name.ToLower() == "wix") valid = true;
                    }
                }
                else
                {
                    // if we have xml but it doesn't include but it does include the wix element, then it is
                    // valid (even though it might be empty)
                    if(document.ChildNodes.Count > 0 && 
                        document.ChildNodes[0].Name.ToLower() == "wix")
                    {
                        valid = true;
                    }
                }
            }

            return valid;
        }

        /// <summary>
        /// Returns all component references based on the components in a particular Wix File
        /// </summary>
        /// <returns></returns>
        public List<ComponentRef> ParseComponentRefs()
        {
            if(IsValidWixXml())
            {
                XmlDocument document = new XmlDocument();
                document.LoadXml(Xml);

                XmlNode wixNode=null;
                foreach (XmlNode node in document.ChildNodes)
                {
                    if(node.Name.ToLower()=="wix")
                    {
                        wixNode = node;
                        break;
                    }
                }

                if(wixNode!=null)
                {
                    XmlNode mainNode=null;
                    foreach (XmlNode node in wixNode.ChildNodes)
                    {
                        if (node.Name.ToLower() == "module" ||
                            node.Name.ToLower() == "product" ||
                            node.Name.ToLower() == "fragment")
                        {
                            mainNode = node;
                            break;
                        }
                    }

                    if(mainNode!=null)
                    {
                        IterateXmlFile(mainNode);
                    }
                }
            }
            else
            {
                throw new ArgumentOutOfRangeException("Invalid Wix XML file.");
            }

            return ComponentRefs;
        }

        private void IterateXmlFile(XmlNode node)
        {
            if(node.HasChildNodes)
            {
                foreach(XmlNode n2 in node.ChildNodes)
                {
                    if (n2.Name.ToLower() == "component")
                    {
                        ComponentRef component = new ComponentRef(n2.Attributes["Id"].Value);
                        if (node.Attributes.GetNamedItem("Primary") != null)
                        {
                            component.Primary =
                                (YesNoType)Enum.Parse(typeof(YesNoType), n2.Attributes["Primary"].Value);
                        }
                        else
                        {
                            component.Primary = YesNoType.no;
                        }
                        ComponentRefs.Add(component);
                    }
                    else if(n2.Name.ToLower() == "directory" || n2.Name.ToLower() == "directoryref")
                    {
                        IterateXmlFile(n2);
                    }
                }
            }
            else
            {
                if (node.Name.ToLower() == "component")
                {
                    ComponentRef component = new ComponentRef(node.Attributes["Id"].Value);
                    if (node.Attributes.GetNamedItem("Primary") != null)
                    {
                        component.Primary =
                            (YesNoType)Enum.Parse(typeof(YesNoType), node.Attributes["Primary"].Value);
                    }
                    else
                    {
                        component.Primary = YesNoType.no;
                    }
                    ComponentRefs.Add(component);
                }
                else if (node.Name.ToLower() == "directory" || node.Name.ToLower() == "directoryref")
                {
                    IterateXmlFile(node);
                }

            }
        }
    }
}
